Notebook di processamento delle immagini di Image Net. Obiettivo è realizzare un batch input che, sfruttando il meccasnismo a code descritto in Tensorflow, fornisca batch della dimensione desiderata per il numero di epoche desiderato.
Viene inoltre sfruttanto l'algoritmo di Inception preprocessing per fornire in input immagini della dimensione corretta con le correzioni preaddestramento fornite da Tensorflow
In [1]:
!rm -rf /tmp/ImageNetTrainTransfer
In [2]:
#Import
import pandas as pd
import numpy as np
import os
import tensorflow as tf
import random
from PIL import Image
#Inception preprocessing code from https://github.com/tensorflow/models/blob/master/slim/preprocessing/inception_preprocessing.py
#useful to maintain training dimension
from utils import inception_preprocessing
import sys
#from inception import inception
'''
Uso di slim e nets_factory (come per SLIM Tensorflow https://github.com/tensorflow/models/blob/master/slim/train_image_classifier.py)
per il ripristino della rete.
Le reti devono essere censite in nets_factory (v. struttura file nella directory di questo notebook)
'''
slim = tf.contrib.slim
from nets import nets_factory
In [3]:
#Global Variables
IMAGE_NET_ROOT_PATH = '/home/carnd/transfer-learning-utils/tiny-imagenet-200/'
#IMAGE_NET_ROOT_PATH = '/data/lgrazioli/'
IMAGE_NET_LABELS_PATH = IMAGE_NET_ROOT_PATH + 'words.txt'
IMAGE_NET_TRAIN_PATH = IMAGE_NET_ROOT_PATH + 'train/'
TRAINING_CHECKPOINT_DIR = '/tmp/ImageNetTrainTransfer'
#Transfer learning CHECKPOINT PATH
#File ckpt della rete
CHECKPOINT_PATH = '/home/carnd/transfer-learning-utils/inception_v4.ckpt'
In [4]:
#Reading label file as Panda dataframe
labels_df = pd.read_csv(IMAGE_NET_LABELS_PATH, sep='\\t', header=None, names=['id','labels'])
labels_df.head(5)
Out[4]:
In [5]:
labels_df.count()
Out[5]:
Aggiunta colonna di lunghezza del label (quante classi contiene ogni label).
In [6]:
#new_labels = []
labels_lengths = []
for idx, row in labels_df.iterrows():
#Convertire a stringa perchè alcuni sono float
current_labels = tuple(str(row['labels']).split(','))
#new_labels.append(current_labels)
labels_lengths.append(len(current_labels))
In [7]:
labels_df['labels_length'] = labels_lengths
labels_indices = [idx for idx, _ in labels_df.iterrows()]
labels_df['indices'] = labels_indices
In [8]:
labels_df.head(20)
Out[8]:
Panda Dataframe che contiene i path di tutte le immagini, la relativa classe, id dell'immagine e classe. La classe viene ottenuta tramite lookup su labels_df (tale operazione pesa molto in termini di tempi di esecuzione)
Può richiedere del tempo. Per lanciare su un campione si può bloccare a un determinato valore di idx
In [9]:
train_paths = []
for idx, label_dir in enumerate(os.listdir(IMAGE_NET_TRAIN_PATH)):
image_dir_path = IMAGE_NET_TRAIN_PATH + label_dir + '/images/'
print("Processing label {0}".format(label_dir))
for image in os.listdir(image_dir_path):
#Estrazione class_id
class_id = image.split('.')[0].split('_')[0]
#Lookup su labels df
target_label = labels_df[labels_df['id'] == class_id] #=> pass to tf.nn.one_hot
#Estrazione del label
target_label = target_label['labels'].values[0]
train_paths.append((image_dir_path + image,
class_id,
image.split('.')[0].split('_')[1],
target_label
))
if idx == 10:
break
train_df = pd.DataFrame(train_paths, columns=['im_path','class', 'im_class_id', 'target_label'])
print(train_df.count())
train_df.head()
Out[9]:
Pulizia delle immagini che non sono nel formato desiderato da inception_preprocessing (3 canali). Operazione lunga!
In [10]:
#Remove black and white images
uncorrect_images = 0
#Salvataggio indici di immagini da eliminare
to_remove_indexes = []
for idx, record in train_df.iterrows():
#Leggo immagine come np.array
im_array = np.array(Image.open(record['im_path']))
#Se non ha 3 canali la aggiungo a quelle da eliminare
if im_array.shape[-1] != 3:
uncorrect_images += 1
to_remove_indexes.append(idx)
if idx % 20 == 0:
sys.stdout.write("\rProcessed {0} images".format(idx))
sys.stdout.flush()
#Rimozione righe identificate
train_df = train_df.drop(train_df.index[to_remove_indexes])
print("New size: {0}".format(len(train_df)))
print("Removed {0} images".format(uncorrect_images))
In [11]:
#Eventuale campionamento da passare al generatore input
example_file_list = list(train_df.im_path)
print(len(example_file_list))
Definizione dizionario dei labels {label: indice}
In [12]:
labels_dict = {}
unique_labels = set(labels_df['labels'])
for idx, target in enumerate(unique_labels):
labels_dict[target] = idx
num_classes = len(labels_dict)
num_classes
Out[12]:
Costruzione lista dei label (stesso ordine della lista di file)
In [13]:
example_label_list = []
for idx, value in train_df.iterrows():
example_label_list.append(labels_dict[value['target_label']])
len(example_label_list)
Out[13]:
In [14]:
num_classes = len(set(example_label_list))
num_classes
Out[14]:
In [15]:
reducted_label_dict = {}
for idx,value in enumerate(set(example_label_list)):
reducted_label_dict[value] = idx
for idx,label in enumerate(example_label_list):
example_label_list[idx] = reducted_label_dict[label]
In [16]:
'''
get_network_fn for returning the corresponding network function.
Se num_classes è da cambiare, impostare is_training a True
Ritorna la funzione definita nel corrispetivo file della rete
'''
model_name = 'inception_v4'
inception_net_fn = nets_factory.get_network_fn(model_name,
num_classes=1001,
is_training = False
)
'''
with tf.device('/gpu:0'):
sampl_input = tf.placeholder(tf.float32, [None, 300,300, 3], name='incpetion_input_placeholder')
#Invocazione della model fn per la definizione delle variabili della rete
#Usa questi tensori che sono quelli per i quali passa il modello
#Necessario per ripristinare il grafo
print(inception_net_fn(sampl_input))
'''
Out[16]:
In [17]:
EPOCHS = 50
BATCH_SIZE = 32
#Serve per capire quando il generatore è passato a batch appartenenti a una nuova epoca
BATCH_PER_EPOCH = np.ceil(len(example_file_list) / BATCH_SIZE)
def parse_single_image(filename_queue):
#Dequeue a file name from the file name queue
#filename, y = filename_queue.dequeue()
#Non bisogna invocare il dequeue il parametro della funziona è già lo scodamento
filename, y = filename_queue[0], filename_queue[1]
#A y manca solo il one-hot
y = tf.one_hot(y, num_classes)
#Read image
raw = tf.read_file(filename)
#convert in jpg (in GPU!)
jpeg_image = tf.image.decode_jpeg(raw)
#Preprocessing with inception preprocessing
jpeg_image = inception_preprocessing.preprocess_image(jpeg_image, 300, 300, is_training=True)
return jpeg_image, y
#jpeg_image = parse_single_image(filename_queue)
def get_batch(filenames, labels, batch_size, num_epochs=None):
#Coda lettura file, slice_input_producer accetta una lista di liste (stessa dimensione)
#Risultato dello scodamento è l'elemento corrente di ciascuna delle liste
#Le liste sono rispettivamente la lista di file e la lista dei label
filename_queue = tf.train.slice_input_producer([filenames, labels])
#Lettura singolo record
jpeg_image,y = parse_single_image(filename_queue)
# min_after_dequeue defines how big a buffer we will randomly sample
# from -- bigger means better shuffling but slower start up and more
# memory used.
# capacity must be larger than min_after_dequeue and the amount larger
# determines the maximum we will prefetch. Recommendation:
# min_after_dequeue + (num_threads + a small safety margin) * batch_size
min_after_dequeue = 10
capacity = min_after_dequeue + 3 * batch_size
#tensors è la lista dei tensori delle single feature e immagini. Esegue batch_size volte i tensori example e label per ottenere il batch
#num_threads incrementa effettivamente l'utilizzo della CPU (confermato dal throughput visisible sul cloudera manager,
#resta comunque un throughput lento ....
example_batch = tf.train.shuffle_batch(
tensors=[jpeg_image, y], batch_size=batch_size, capacity=capacity,
min_after_dequeue=min_after_dequeue, allow_smaller_final_batch=True, num_threads=2)
return example_batch
#TF Graph, per ora recupera solamente un batch
with tf.device('/cpu:0'):
with tf.name_scope('preprocessing') as scope:
x,y = get_batch(example_file_list, example_label_list, batch_size=BATCH_SIZE)
#x = tf.contrib.layers.flatten(x)
with tf.device('/gpu:0'):
#inception prelogits
inception_net_fn(x)
#prelogits = tf.placeholder(tf.float32, [None, 1536], name='prelogits_placeholder')
prelogits = tf.get_default_graph().get_tensor_by_name("InceptionV4/Logits/PreLogitsFlatten/Reshape:0")
with tf.device('/gpu:0'):
with tf.variable_scope('trainable'):
'''with tf.variable_scope('hidden') as scope:
hidden = tf.layers.dense(
prelogits,
units=128,
activation=tf.nn.relu
)'''
#Kenerl init None = glooroot initializers (sttdev = 1/sqrt(n))
with tf.variable_scope('readout') as scope:
output = tf.layers.dense(
prelogits,
units=num_classes,
activation=None
)
with tf.variable_scope('train_op') as scope:
# Define loss and optimizer
targetvars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, "trainable")
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=output, labels=y))
optimizer = tf.train.AdamOptimizer(learning_rate=0.001).minimize(cost, var_list=targetvars)
# Accuracy
correct_pred = tf.equal(tf.argmax(output, 1), tf.argmax(y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
tf.summary.scalar('accuracy', accuracy)
tf.summary.scalar('loss', cost)
init = tf. global_variables_initializer()
merged_summeries = tf.summary.merge_all()
In [18]:
#GPU config
config = tf.ConfigProto(log_device_placement=True)
config.gpu_options.allow_growth = True
#Saver per restoring inception net
saver = tf.train.Saver()
with tf.Session(config=config) as sess:
sess.run(init)
writer = tf.summary.FileWriter(TRAINING_CHECKPOINT_DIR,
sess.graph)
#Start populating the filename queue.
coord = tf.train.Coordinator()
#Senza questa chiamata non partono i thread per popolare la coda che permette di eseguire la read
threads = tf.train.start_queue_runners(coord=coord)
#Current epoch and step servono a capire quando cambiare epoca e quando fermarsi
current_epoch = 0
current_step = 0
while current_epoch < EPOCHS:
x_batch, y_batch = sess.run([x,y])
#Forward pass nella incpetion net
#inception_pre_logits = sess.run(tf.get_default_graph().get_tensor_by_name("InceptionV4/Logits/PreLogitsFlatten/Reshape:0"),
#feed_dict={sampl_input: x_batch})
sess.run(optimizer, feed_dict={x: x_batch, y: y_batch})
#print(x_batch.shape)
if current_step % 10 == 0:
#print("Batch shape {}".format(x_batch.shape))
print("Current step: {0}".format(current_step))
train_loss, train_accuracy, train_summ = sess.run([cost,accuracy,merged_summeries],
feed_dict={x: x_batch, y: y_batch})
print("Loss: {0} accuracy {1}".format(train_loss, train_accuracy))
writer.add_summary(train_summ, current_epoch * current_step + 1)
#Cambiare epoca, raggiunto il massimo per l'epoca corrente
if current_step == (BATCH_PER_EPOCH - 1):
current_epoch += 1
current_step = 0
print("EPOCH {0}".format(current_epoch))
#Epoche terminate -> chiudere
if current_epoch >= EPOCHS:
break
if current_step == 0 and current_epoch == 0:
writer.add_graph(sess.graph)
#train_summary = sess.run([merged_summeries], feed_dict={x: x_batch, y: y_batch})
#writer.add_summary(train_summary, current_step)
current_step += 1
#for i in range(10):
#converted_im = sess.run(jpeg_image)
#print(converted_im.shape)
#Chiusura del coordinator (chiudi i thread di lettura)
coord.request_stop()
coord.join(threads)
sess.close()
In [25]:
tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, "trainable")
Out[25]:
In [ ]: